#include "stdafx.h"

char* Array_AimbotType[0x2] = { "Visible", "Autowall" };
char* Array_TargetType[0x2] = { "Snap", "Silent" };
char* Array_TargetBone[0x10] = { "j_helmet", "j_head", "j_neck", "j_shoulder_le", "j_shoulder_ri", "j_mainroot", "j_elbow_le", "j_elbow_ri", "j_wrist_le", "j_wrist_ri", "j_spineupper", "j_spinelower", "j_knee_le", "j_knee_ri", "j_ankle_le", "j_ankle_ri" };

short sCachedTags[0x10] = { NULL };
int sCachedRiotShield = NULL;

bool bClientsHitable[18] = { false };

vec2 spinAngles = vec2();

bool IsClientWallbangable(int clientNum, short tag) {
	vec3 end = GetTagPos(&get_centity(clientNum), tag);

	vec3 viewOrigin = GetPlayerViewOrigin();

	BulletFireParams bulletFireParams;
	BulletTraceResults bulletTraceResults;

	bulletFireParams.iIgnoreEntIndex = get_cg()->clientNumber;
	bulletFireParams.iWeaponEntIndex = 0x3FE;
	bulletFireParams.fDamageMultiplier = 1.0f;
	bulletFireParams.iMethodOfDeath = 2;
	bulletFireParams.vOrigStart = viewOrigin;
	bulletFireParams.vStart = viewOrigin;
	bulletFireParams.vEnd = end;

	vec3 endDirection = end - viewOrigin;

	vec3 angle;
	VectorToAngle(&endDirection, &angle);

	vec3 hax;
	AngleVectors(&angle, &hax, nullptr, nullptr);

	bulletFireParams.vDirection = hax;

	// Disable the bullet traces
	*(QWORD*)0x822DFB90 = 0x386000004E800020; // DynEntCl_DynEntImpactEvent
	*(QWORD*)0x82258520 = 0x386000004E800020; // CG_BulletHitEvent
	*(DWORD*)0x82257E30 = 0x48000218;

	// Disable BG_HasPerk's check for FMJ to fix some spots
	*(DWORD*)0x82258CE4 = 0x38600000;
	*(DWORD*)0x82258FAC = 0x38600000;
	*(DWORD*)0x8225900C = 0x38600000;

	// Allow 360 degree autowall checks
	*(DWORD*)0x82258D60 = 0x38600001;

	// Disable the games DrawBulletImpacts call
	*(DWORD*)0x82259B40 = 0x60000000;

	FireBulletPenetrate(0, &bulletFireParams, get_centity(get_cg()->clientNumber).weaponNum, &get_centity(get_cg()->clientNumber), viewOrigin, 0, 1, &bulletTraceResults);

	// Re-enable the bullet traces
	*(QWORD*)0x822DFB90 = 0x7D8802A69181FFF8; // DynEntCl_DynEntImpactEvent
	*(QWORD*)0x82258520 = 0x7D8802A6486BDA55; // CG_BulletHitEvent
	*(DWORD*)0x82257E30 = 0x419A00AC;

	// Re-enable BG_HasPerk's check for FMJ
	*(DWORD*)0x82258CE4 = 0x4BFA1B15;
	*(DWORD*)0x82258FAC = 0x4BFA184D;
	*(DWORD*)0x8225900C = 0x4BFA17ED;

	// Unpatch 360 degree autowall check
	*(DWORD*)0x82258D60 = 0x4BFF4301;

	// Unpatch the games DrawBulletImpacts call
	*(DWORD*)0x82259B40 = 0x4BFFF669;

	// Check if the trace hit a shield
	if (bulletTraceResults.Trace.partGroup == 0x14) {
		return false;
	}

	return (bulletFireParams.iIgnoreEntIndex == clientNum);
}

double Radians(float Degrees) {
	return  0.01745329238474369 * Degrees;
}

void GetWeaponSpread(int* seed, float* x, float* y) {
	float min, max, spread, fx, fy;
	int randSeed = *seed;
	BG_GetSpreadForWeapon(&get_cg()->playerState, get_centity(get_cg()->clientNumber).weaponNum, &min, &max);
	spread = (max - min) * (get_cg()->SpreadScale * 0.00392156862) + min;
	fx = Radians(RandomBulletDir(&randSeed) * 360);
	sub_82697FC0(&randSeed);
	fy = RandomBulletDir(&randSeed);
	*x = cos(fx) * fy * spread;
	*y = sin(fx) * fy * spread;
}

void NoSpread(usercmd_s* nextCmd, usercmd_s* currentCmd) {
	vec3 SpreadAngles = vec3();

	int Seed = nextCmd->serverTime;

	TransformSeed(&Seed);
	GetWeaponSpread(&Seed, &SpreadAngles.x, &SpreadAngles.y);

	currentCmd->ViewAngles[0] -= ANGLE2SHORT(-SpreadAngles.y);
	currentCmd->ViewAngles[1] -= ANGLE2SHORT(-SpreadAngles.x);
}

void FixMovement(usercmd_s* pCmd, float CurAngle, float OldAngle, float fOldForward, float fOldSidemove) {
	float deltaView = CurAngle - OldAngle;
	int forward = (int)(cos(deltaView * (PI / 180.0f)) * fOldForward + cos((deltaView + 90.f) * (PI / 180.0f)) * fOldSidemove);
	int right = (int)(sin(deltaView * (PI / 180.0f)) * fOldForward + sin((deltaView + 90.f) * (PI / 180.0f)) * fOldSidemove);

	if (forward < -128) forward = -128;
	else if (forward > 127) forward = 127;
	if (right < -128) right = -128;
	else if (right > 127) right = 127;

	pCmd->forwardmove = (char)forward;
	pCmd->sidemove = (char)right;
}

void NormalAim(vec3 toAngle) {
	clientActive_s* clientActive = get_clientActive();

	if (clientActive) {
		clientActive->viewAngles.x = toAngle.x - clientActive->spawnAngles.x;
		clientActive->viewAngles.y = toAngle.y - clientActive->spawnAngles.y;
	}
}

void SilentAim(usercmd_s* oldCommand, vec3 toAngle) {
	clientActive_s* clientActive = get_clientActive();

	if (clientActive) {
		float oldAngle = SHORT2ANGLE(oldCommand->ViewAngles[1]);

		oldCommand->ViewAngles[0] = ANGLE2SHORT(toAngle.x - clientActive->spawnAngles.x);
		oldCommand->ViewAngles[1] = ANGLE2SHORT(toAngle.y - clientActive->spawnAngles.y);

		FixMovement(oldCommand, SHORT2ANGLE(oldCommand->ViewAngles[1]), oldAngle, oldCommand->forwardmove, oldCommand->sidemove);
	}
}

void ApplyPrediction(int clientNum, vec3 origin) {
	vec3 trDelta = (get_centity(clientNum).GetNextPredictedPosition() - get_centity(clientNum).GetPreviousPredictedPosition());

	origin.x = ((origin.x + trDelta.x) * (get_cg()->snap->Ping * 0.001f));
	origin.y = ((origin.y + trDelta.y) * (get_cg()->snap->Ping * 0.001f));
	origin.z = ((origin.z + trDelta.z) * (get_cg()->snap->Ping * 0.001f));
}

int GetClosestClient() {
	int dwClosestClient = -1;
	float closestDistance = FLT_MAX;

	for (int i = 0; i < 18; i++) {
		if (i == get_cg()->clientNumber) continue;
		if (get_centity(i).eType != 1) continue;
		if ((*(int*)((DWORD)&get_centity(i) + 0x36C) & 0x40000000) == 0 || (*(int*)((DWORD)&get_centity(i) + 0x1D4) & 0x40000)) continue;
		if (!IsEnemy(i)) continue;
		if (sCachedTags[MenuOptions::dwTargetBone] == NULL) continue;

		if (MenuOptions::dwAimbotType == 1) {
			bClientsHitable[i] = IsClientVisible(i, sCachedTags[MenuOptions::dwTargetBone]);
			if (!bClientsHitable[i]) {
				bClientsHitable[i] = IsClientWallbangable(i, sCachedTags[MenuOptions::dwTargetBone]);
				if (!bClientsHitable[i])
					continue;
			}
		}
		else {
			bClientsHitable[i] = IsClientVisible(i, sCachedTags[MenuOptions::dwTargetBone]);
			if (!bClientsHitable[i])
				continue;
		}

		FLOAT TempDistance = get_centity(get_cg()->clientNumber).Origin.distanceTo(get_centity(i).Origin);
		if (TempDistance < closestDistance)
		{
			dwClosestClient = i;
			closestDistance = TempDistance;
		}
	}

	return dwClosestClient;
}

void HandleAimbot(usercmd_s* currentCmd, usercmd_s* nextCmd) {
	if (MenuOptions::bAimbot) {
		if (sCachedTags[MenuOptions::dwTargetBone] != NULL && sCachedRiotShield != NULL) {
			int closestClient = GetClosestClient();
			if (closestClient != -1) {
				vec3 viewOrigin = GetPlayerViewOrigin();
				if (!viewOrigin.IsNull()) {
					vec3 bonePosition = GetTagPos(&get_centity(closestClient), sCachedTags[MenuOptions::dwTargetBone]);
					if (!bonePosition.IsNull()) {
						if (MenuOptions::bPingPrediction)
							ApplyPrediction(closestClient, bonePosition);

						vec3 subtracted = (bonePosition - viewOrigin);

						vec3 angle;
						VectorToAngle(&subtracted, &angle);

						if (MenuOptions::dwTargetType == 0) {
							NormalAim(angle);
						}
						else if (MenuOptions::dwTargetType == 1) {
							SilentAim(currentCmd, angle);
						}

						if (MenuOptions::bNoSpread)
							NoSpread(nextCmd, currentCmd);

						if (MenuOptions::bAutoShoot && get_centity(get_cg()->clientNumber).weaponNum != sCachedRiotShield) {
							nextCmd->buttons |= 0x80000000;
							if (currentCmd->buttons & 0x80000000) {
								currentCmd->buttons &= 0x7FFFFFFFu;
							}
						}
					}
				}
			}
		}
	}
}

int FindBestAntiAimTarget()
{
	int dwClosestClient = -1;
	float closestDistance = FLT_MAX;

	for (int i = 0; i < 18; i++) {
		if (i == get_cg()->clientNumber) continue;
		if (get_centity(i).eType != 1) continue;
		if ((*(int*)((DWORD)&get_centity(i) + 0x36C) & 0x40000000) == 0 || (*(int*)((DWORD)&get_centity(i) + 0x1D4) & 0x40000)) continue;
		if (!IsEnemy(i)) continue;

		FLOAT TempDistance = get_centity(get_cg()->clientNumber).Origin.distanceTo(get_centity(i).Origin);
		if (TempDistance < closestDistance)
		{
			dwClosestClient = i;
			closestDistance = TempDistance;
		}
	}

	return dwClosestClient;
}

void HandleSpinbot(usercmd_s* currentCmd) {
	if ((*(BYTE*)((DWORD)get_cg() + 0x18F) & 0x8) || (get_cg()->Health <= 0) || currentCmd->weapon == 0 || currentCmd->buttons & 0x00010000 || currentCmd->buttons & 0x00020000)
		return;

	if (MenuOptions::bAntiAim) {
		int AAClient = FindBestAntiAimTarget();
		if (AAClient != -1) {
			vec3 viewOrigin = GetPlayerViewOrigin();

			vec3 pos = GetTagPos(&get_centity(AAClient), SL_GetString("j_mainroot", 0));
			vec3 subtracted = pos - viewOrigin;

			vec3 angle;
			VectorToAngle(&subtracted, &angle);

			spinAngles.y = (angle.y - 180.0f) - get_cg()->playerState.deltaAngles.y;
			currentCmd->ViewAngles[1] = ANGLE2SHORT(spinAngles.y);
		}

		spinAngles.x = -69.99f - get_cg()->playerState.deltaAngles.x;
		currentCmd->ViewAngles[0] = ANGLE2SHORT(spinAngles.x);
	}
}